package com.ym521.logdog.engine

import android.content.Context
import android.content.Intent
import androidx.core.content.FileProvider
import com.ym521.logdog.core.*
import com.ym521.logdog.utils.DateUtil
import com.ym521.logdog.utils.LogFormatUtil
import java.io.File

/**
 * logdog(日志狗)
 * 日志狗核心引擎
 * 实现对日志信息的分发
 */
internal class LogDogEngine private constructor(private val jsonEngine: IJsonEngine) :
    ILogDogEngine {
    private lateinit var logPrinter: IPrintLogEngine
    private lateinit var logDiskWrite: IDiskLogEngine
    private lateinit var crashLogDiskWrite: IDiskLogEngine

    private constructor(mContext: Context, jsonEngine: IJsonEngine) : this(jsonEngine) {
        initPrintLogEngine()
        initDiskLogWriteEngine(mContext)
    }


    companion object {
        fun build(mContext: Context, jsonEngine: IJsonEngine): ILogDogEngine {
            return LogDogEngine(mContext, jsonEngine)
        }
    }

    private fun initPrintLogEngine() {
        logPrinter = PrintLogEngine.build()
    }

    private fun initDiskLogWriteEngine(mContext: Context) {
        logDiskWrite = DiskLogEngineFactory.build(false, mContext)
        crashLogDiskWrite = DiskLogEngineFactory.build(true, mContext)
    }

    override fun <T> debug(tag: String, msg: T) {
        val msgContent = toMsgContent(msg)
        logDogEnable(LogPriority.DEBUG, tag, msgContent)
    }

    override fun debug(tag: String, msgFormat: String, vararg objs: Any) {
        logDogEnableFormat(LogPriority.DEBUG, tag, msgFormat, *objs)
    }

    override fun <T> info(tag: String, msg: T) {
        val msgContent = toMsgContent(msg)
        logDogEnable(LogPriority.INFO, tag, msgContent)
    }

    override fun info(tag: String, msgFormat: String, vararg objs: Any) {
        logDogEnableFormat(LogPriority.INFO, tag, msgFormat, *objs)
    }

    override fun <T> warn(tag: String, msg: T) {
        val msgContent = toMsgContent(msg)
        logDogEnable(LogPriority.WARN, tag, msgContent)
    }

    override fun warn(tag: String, msgFormat: String, vararg objs: Any) {
        logDogEnableFormat(LogPriority.WARN, tag, msgFormat, *objs)
    }

    override fun <T> error(tag: String, msg: T) {
        val msgContent = toMsgContent(msg)
        logDogEnable(LogPriority.ERROR, tag, msgContent)
    }

    override fun error(tag: String, msgFormat: String, vararg objs: Any) {
        logDogEnableFormat(LogPriority.ERROR, tag, msgFormat, *objs)
    }

    override fun <T> custom(
        priority: LogPriority,
        tag: String,
        msg: T,
        printer: Boolean,
        write: Boolean
    ) {
        val msgContent = toMsgContent(msg)
        logDogCustomEnable(printer, write, priority, tag, msgContent)
    }

    override fun custom(
        printer: Boolean,
        write: Boolean,
        priority: LogPriority,
        tag: String,
        msgFormat: String,
        vararg objs: Any
    ) {
        logDogCustomEnableFormat(printer, write, priority, tag, msgFormat, *objs)
    }

    override fun shareLogFile(mContext: Context, fileType: LogFileType) {
        if (!LogDogConfig.isWriteLogFileEnable) return
        val currentDate = DateUtil.currentDateFile()
        //目录地址
        val fileParentPath = if (LogDogConfig.logFileShow) {
            mContext.getExternalFilesDir("logdog")?.absolutePath
        } else {
            mContext.getDir("logdog", Context.MODE_PRIVATE).absolutePath
        }
        val filePath = when (fileType) {
            LogFileType.LOG_FILE -> fileParentPath + "/${currentDate}.log"
            LogFileType.LOG_CRASH_FILE -> fileParentPath + "/crash_${currentDate}.log"
        }
        if (filePath.isNotBlank()) {
            shareFile(mContext, filePath)
        }
    }

    override fun crashLog(tag: String, msgFormat: String, vararg objs: Any) {
        logDogEnableFormat(LogPriority.CRASH, tag, msgFormat, *objs)
    }

    private fun shareFile(mContext: Context, filePath: String) {
        val file = File(filePath)
        if (!file.exists()) return
        val intent = Intent(Intent.ACTION_SEND)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        val uri = FileProvider.getUriForFile(mContext, mContext.packageName + ".fileshare", file)
        intent.putExtra(Intent.EXTRA_STREAM, uri)
        intent.type = "text/plain" //分享文件类型
        mContext.startActivity(Intent.createChooser(intent, "分享日志"))
    }

    /**
     * 泛型处理
     * 转String 输出
     */
    private fun <T> toMsgContent(msg: T): String {
        val msgContent = when (msg) {
            is String -> msg
            is Char -> "$msg"
            is Int -> "$msg"
            is Byte -> "$msg"
            is Short -> "$msg"
            is Long -> "$msg"
            is Boolean -> "$msg"
            is Double -> "$msg"
            is Float -> "$msg"
            else ->
                jsonEngine.toJSON(msg as Any)
        }
        return msgContent
    }

    /**
     * 携带占位符% 的log 处理
     */
    private fun logDogEnableFormat(
        priority: LogPriority,
        tag: String,
        msgFormat: String,
        vararg objArray: Any
    ) {
        if (!logPrintEnable(priority.priority) && !logWriteEnable(priority.priority)) return
        val jsonArray = Array(objArray.size) { "" }
        try {
            for (index in objArray.indices) {
                jsonArray[index] = toMsgContent(objArray[index])
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        val msgContent = LogFormatUtil.toLogContent(msgFormat, *jsonArray)
        logDogPrintHandlers(priority, tag, msgContent)
        logDogWriteHandlers(priority, tag, msgContent)
    }

    /**
     * 自定义 Log
     */
    private fun logDogCustomEnableFormat(
        printer: Boolean,
        write: Boolean,
        priority: LogPriority,
        tag: String,
        msgFormat: String,
        vararg objArray: Any
    ) {
        val jsonArray = Array(objArray.size) { "" }
        try {
            for (index in objArray.indices) {
                jsonArray[index] = toMsgContent(objArray[index])
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        val msgContent = LogFormatUtil.toLogContent(msgFormat, *jsonArray)
        logDogCustomHandlers(printer, write, priority, tag, msgContent)

    }

    /**
     * 正常log 处理
     */
    private fun logDogEnable(priority: LogPriority, tag: String, msg: String) {
        if (!logPrintEnable(priority.priority) && !logWriteEnable(priority.priority)) return

        val msgContent = toMsgContent(msg)

        logDogPrintHandlers(priority, tag, msgContent)
        logDogWriteHandlers(priority, tag, msgContent)
    }

    /**
     * 自定义log 处理
     */
    private fun logDogCustomEnable(
        printer: Boolean, write: Boolean, priority: LogPriority, tag: String, msg: String
    ) {
        val msgContent = toMsgContent(msg)
        logDogCustomHandlers(printer, write, priority, tag, msgContent)
    }

    /**
     * log打印处理
     */
    private fun logDogPrintHandlers(priority: LogPriority, tag: String, msg: String) {
        if (!logPrintEnable(priority.priority)) return
        logPrinter.logPrint(priority, tag, msg)
    }

    /**
     * log写入处理
     */
    private fun logDogWriteHandlers(priority: LogPriority, tag: String, msg: String) {
        if (!logWriteEnable(priority.priority)) return

        if (LogDogConfig.isWriteLogFileEnable) {
            logDiskWrite.logWrite(priority, tag, msg)
        }
    }

    /**
     * 是否允许打印的日志等级
     * 日志等级过滤
     */
    private fun logPrintEnable(priority: Int): Boolean {
        return (LogDogConfig.logFilter and priority) == 0
    }

    /**
     * 是否允许写入的日志等级
     * 日志等级过滤
     */
    private fun logWriteEnable(priority: Int): Boolean {
        return (LogDogConfig.logWriteFilter and priority) == 0
    }


    /**
     * 自定义 log 处理
     */
    private fun logDogCustomHandlers(
        printer: Boolean,
        write: Boolean,
        priority: LogPriority,
        tag: String,
        msg: String
    ) {
        if (printer) {
            logPrinter.logPrint(priority, tag, msg)
        }
        if (write) {
            if (priority == LogPriority.CRASH)
                crashLogDiskWrite.logWrite(priority, tag, msg)
            else
                logDiskWrite.logWrite(priority, tag, msg)
        }
    }
}